// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Solaris系统调用。
// 该文件编译为普通Go代码，
// 但它也是mksyscall的输入，
// 解析
// 请注意，有时我们使用小写的
// syscall_solaris.go或syscall_unix.go中。

package syscall

import "unsafe"

// 在asm_solaris_amd64.s中实现。
func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)

type SockaddrDatalink struct {
	Family uint16
	Index  uint16
	Type   uint8
	Nlen   uint8
	Alen   uint8
	Slen   uint8
	Data   [244]int8
	raw    RawSockaddrDatalink
}

func direntIno(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}

func direntReclen(buf []byte) (uint64, bool) {
	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}

func direntNamlen(buf []byte) (uint64, bool) {
	reclen, ok := direntReclen(buf)
	if !ok {
		return 0, false
	}
	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}

func pipe() (r uintptr, w uintptr, err uintptr)

func Pipe(p []int) (err error) {
	if len(p) != 2 {
		return EINVAL
	}
	r0, w0, e1 := pipe()
	if e1 != 0 {
		err = Errno(e1)
	}
	p[0], p[1] = int(r0), int(w0)
	return
}

func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
	if sa.Port < 0 || sa.Port > 0xFFFF {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_INET
	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
	p[0] = byte(sa.Port >> 8)
	p[1] = byte(sa.Port)
	for i := 0; i < len(sa.Addr); i++ {
		sa.raw.Addr[i] = sa.Addr[i]
	}
	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil
}

func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
	if sa.Port < 0 || sa.Port > 0xFFFF {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_INET6
	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
	p[0] = byte(sa.Port >> 8)
	p[1] = byte(sa.Port)
	sa.raw.Scope_id = sa.ZoneId
	for i := 0; i < len(sa.Addr); i++ {
		sa.raw.Addr[i] = sa.Addr[i]
	}
	return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil
}

func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
	name := sa.Name
	n := len(name)
	if n >= len(sa.raw.Path) {
		return nil, 0, EINVAL
	}
	sa.raw.Family = AF_UNIX
	for i := 0; i < n; i++ {
		sa.raw.Path[i] = int8(name[i])
	}
	// 长度为族（uint16），名称，NUL。
	sl := _Socklen(2)
	if n > 0 {
		sl += _Socklen(n) + 1
	}
	if sa.raw.Path[0] == '@' {
		sa.raw.Path[0] = 0
		// 不计算抽象地址的尾随NUL。
		sl--
	}

	return unsafe.Pointer(&sa.raw), sl, nil
}

func Getsockname(fd int) (sa Sockaddr, err error) {
	var rsa RawSockaddrAny
	var len _Socklen = SizeofSockaddrAny
	if err = getsockname(fd, &rsa, &len); err != nil {
		return
	}
	return anyToSockaddr(&rsa)
}

const ImplementsGetwd = true

// sys Getcwd（buf[]字节）（n int，error error）

func Getwd() (wd string, err error) {
	var buf [PathMax]byte
	// Getcwd如果由于任何原因失败，将返回一个错误。
	_, err = Getcwd(buf[0:])
	if err != nil {
		return "", err
	}
	n := clen(buf[:])
	if n < 1 {
		return "", EINVAL
	}
	return string(buf[:n]), nil
}

/*
 * Wrapped
 */

// sysnb getgroups（ngid int，gid*_gid_t）（n int，error error）
// sysnb setgroups（ngid int，gid*_gid_t）（error）

func Getgroups() (gids []int, err error) {
	n, err := getgroups(0, nil)
	if err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, nil
	}

	// 健全性检查组计数。BSD上的最大值为16。
	if n < 0 || n > 1000 {
		return nil, EINVAL
	}

	a := make([]_Gid_t, n)
	n, err = getgroups(n, &a[0])
	if err != nil {
		return nil, err
	}
	gids = make([]int, n)
	for i, v := range a[0:n] {
		gids[i] = int(v)
	}
	return
}

func Setgroups(gids []int) (err error) {
	if len(gids) == 0 {
		return setgroups(0, nil)
	}

	a := make([]_Gid_t, len(gids))
	for i, v := range gids {
		a[i] = _Gid_t(v)
	}
	return setgroups(len(a), &a[0])
}

func ReadDirent(fd int, buf []byte) (n int, err error) {
	// 最后一个参数是（basep*uintpttr），系统调用不接受nil。
	// TODO（rsc）：我们可以对所有调用使用一个全局basep吗？
	return Getdents(fd, buf, new(uintptr))
}

// 等待状态为底部7位，0（已退出）、
// 0x7F（已停止）或导致退出的信号号。
// 0x80位表示是否存在内核转储。
// 一个额外的数字（退出代码、导致停止的信号）
// 位于高位。

type WaitStatus uint32

const (
	mask  = 0x7F
	core  = 0x80
	shift = 8

	exited  = 0
	stopped = 0x7F
)

func (w WaitStatus) Exited() bool { return w&mask == exited }

func (w WaitStatus) ExitStatus() int {
	if w&mask != exited {
		return -1
	}
	return int(w >> shift)
}

func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != 0 }

func (w WaitStatus) Signal() Signal {
	sig := Signal(w & mask)
	if sig == stopped || sig == 0 {
		return -1
	}
	return sig
}

func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }

func (w WaitStatus) Stopped() bool { return w&mask == stopped && Signal(w>>shift) != SIGSTOP }

func (w WaitStatus) Continued() bool { return w&mask == stopped && Signal(w>>shift) == SIGSTOP }

func (w WaitStatus) StopSignal() Signal {
	if !w.Stopped() {
		return -1
	}
	return Signal(w>>shift) & 0xFF
}

func (w WaitStatus) TrapCause() int { return -1 }

func wait4(pid uintptr, wstatus *WaitStatus, options uintptr, rusage *Rusage) (wpid uintptr, err uintptr)

func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
	r0, e1 := wait4(uintptr(pid), wstatus, uintptr(options), rusage)
	if e1 != 0 {
		err = Errno(e1)
	}
	return int(r0), err
}

func gethostname() (name string, err uintptr)

func Gethostname() (name string, err error) {
	name, e1 := gethostname()
	if e1 != 0 {
		err = Errno(e1)
	}
	return name, err
}

func UtimesNano(path string, ts []Timespec) error {
	if len(ts) != 2 {
		return EINVAL
	}
	return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
}

// sys fcntl（fd int，cmd int，arg int）（val int，err error）

// FcntlFlock为F_GETLK、F_SETLK或F_SETLKW命令执行fcntl系统调用。
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_fcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(lk)), 0, 0, 0)
	if e1 != 0 {
		return e1
	}
	return nil
}

func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
	switch rsa.Addr.Family {
	case AF_UNIX:
		pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa))
		sa := new(SockaddrUnix)
		// 假设路径以NUL结束。
		// 从技术上讲，这不是
		// 抽象Unix域套接字的Solaris语义——它们被认为是不奇怪的固定大小二进制blob——但
		// 每个人都使用此约定。
		n := 0
		for n < len(pp.Path) && pp.Path[n] != 0 {
			n++
		}
		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
		sa.Name = string(bytes)
		return sa, nil

	case AF_INET:
		pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
		sa := new(SockaddrInet4)
		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
		sa.Port = int(p[0])<<8 + int(p[1])
		for i := 0; i < len(sa.Addr); i++ {
			sa.Addr[i] = pp.Addr[i]
		}
		return sa, nil

	case AF_INET6:
		pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
		sa := new(SockaddrInet6)
		p := (*[2]byte)(unsafe.Pointer(&pp.Port))
		sa.Port = int(p[0])<<8 + int(p[1])
		sa.ZoneId = pp.Scope_id
		for i := 0; i < len(sa.Addr); i++ {
			sa.Addr[i] = pp.Addr[i]
		}
		return sa, nil
	}
	return nil, EAFNOSUPPORT
}

// sys accept（s int，rsa*RawSockaddrAny，addrlen*_Socklen）（fd int，err error）=libsocket.accept 

func Accept(fd int) (nfd int, sa Sockaddr, err error) {
	var rsa RawSockaddrAny
	var len _Socklen = SizeofSockaddrAny
	nfd, err = accept(fd, &rsa, &len)
	if err != nil {
		return
	}
	sa, err = anyToSockaddr(&rsa)
	if err != nil {
		Close(nfd)
		nfd = 0
	}
	return
}

func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
	var msg Msghdr
	var rsa RawSockaddrAny
	msg.Name = (*byte)(unsafe.Pointer(&rsa))
	msg.Namelen = uint32(SizeofSockaddrAny)
	var iov Iovec
	if len(p) > 0 {
		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
		iov.SetLen(len(p))
	}
	var dummy int8
	if len(oob) > 0 {
		// 接收至少一个正常字节
		if len(p) == 0 {
			iov.Base = &dummy
			iov.SetLen(1)
		}
		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
		msg.Accrightslen = int32(len(oob))
	}
	msg.Iov = &iov
	msg.Iovlen = 1
	if n, err = recvmsg(fd, &msg, flags); err != nil {
		return
	}
	oobn = int(msg.Accrightslen)
	// 仅当套接字未连接时才指定源地址
	if rsa.Addr.Family != AF_UNSPEC {
		from, err = anyToSockaddr(&rsa)
	}
	return
}

func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
	_, err = SendmsgN(fd, p, oob, to, flags)
	return
}

// sys sendmsg（s int，msg*Msghdr，flags int）（n int，error）=libsocket.\uuuxnet\u sendmg 

func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
	var ptr unsafe.Pointer
	var salen _Socklen
	if to != nil {
		ptr, salen, err = to.sockaddr()
		if err != nil {
			return 0, err
		}
	}
	var msg Msghdr
	msg.Name = (*byte)(unsafe.Pointer(ptr))
	msg.Namelen = uint32(salen)
	var iov Iovec
	if len(p) > 0 {
		iov.Base = (*int8)(unsafe.Pointer(&p[0]))
		iov.SetLen(len(p))
	}
	var dummy int8
	if len(oob) > 0 {
		// 发送至少一个正常字节
		if len(p) == 0 {
			iov.Base = &dummy
			iov.SetLen(1)
		}
		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
		msg.Accrightslen = int32(len(oob))
	}
	msg.Iov = &iov
	msg.Iovlen = 1
	if n, err = sendmsg(fd, &msg, flags); err != nil {
		return 0, err
	}
	if len(oob) > 0 && len(p) == 0 {
		n = 0
	}
	return n, nil
}

/*
 * Exposed directly
 */
// sys Access（路径字符串，模式uint32）（错误）
// sys Adjtime（delta*Timeval，olddelta*Timeval）（错误）
// sys Chmod（路径字符串，模式uint32）（错误）
// sys CHOOT（路径字符串，uid int，gid int）（错误）
// sys Chroot（路径字符串）（错误）
// sys Close（fd int）（错误）
// sys Dup（fd int）（nfd int，错误）
// sys Fchown（fd int，uid int，gid int）（错误）
// sys Fpathconf（fd int，name int）（val int，error）
// sys Fstat（fd int，stat*stat_t）（error error）
// sysnb Getpid（）（pid int）
// sys Geteuid（）（euid int）
// sys Getegid（）（egid int）
// sys Getppid（）（ppid int）
// sys Getpriority（which int，who int）（n int，error error）
// sysnb Gettimeofday（tv*Timeval）（错误）
// sysnb Getuid（）（uid int）
// sys Kill（pid int，signum Signal）（错误）
// sys Lchown（path string，uid int，gid int）（错误）
// sys Link（path string，Link string）（error）
// sys Lstat（路径字符串，stat*stat\t）（error error）
// sys Mkdir（路径字符串，模式uint32）（error error）
// sys Open（路径字符串，模式int，perm uint32）（fd int，错误）
// sys Pathconf（路径字符串，名称int）（val int，错误）
// sys Pread（fd int，p[]字节，偏移量int64）（n int，错误）
// sys Pwrite（fd int，p[]字节，偏移量int64）（n int，错误）
// /sys Readlink（路径字符串，buf[]字节）（n int，错误）
// sys Rename（从字符串到字符串）（错误错误）
// sys Rmdir（路径字符串）（错误）
// sys Seek（fd int，offset int64，whence int）（newoffset int64，error）=lseek 
// sys sendfile（outd int，infd int，offset*int64，count int）（writed int，error）=libsendfile.sendfile 
// sysnb Setegid（egid int）（错误）
// sysnb Seteuid（euid int）（错误）
// sysnb Setgid（gid int）（错误）
// sysnb Setregid（rgid int，egid int）（error error）
// sysnb Setreuid（ruid int，euid int）（error error）
// sysnb Setrlimit（which int，lim*Rlimit）（error error）
// sys Stat（path string，Stat*Stat_t）（error error）
// sys Symlink（path string，link string）（error error）
// sys Sync（）（error error）
// sys Truncate（path string，length int64）（error）
// sys Ftruncate（fd int，length int64）（错误）
// sys Umask（newmask int）（oldmak int）
// sys Unlink（path string）（error）
// sys utimes（path string，times*[2]Timeval）（error）
// sys bind（s int，addr unsafe.Pointer，addrlenSocklen）（error）=libsocket.\uuuxnet\u bind 
// sys connect（s int，addr unsafe.Pointer，addrlen(错误）=libsocket.\uxnet\u connect 
// sys mmap（addr uintpttr，length uintpttr，prot int，flag int，fd int，pos int64）（ret uintpr，error）
// sys sendto（s int，buf[]byte，flags int，to unsafe.Pointer，addrlen(error error）=libsocket.\uu xnet\u sendto 
// sys socket（domain int，typ int，proto int）（fd int，error error）=libsocket.\uu xnet\u socket 
// sys write（fd int，p[]字节）（n int，error error）
// /sys getsockopt（s int，level int，name int，val unsafe.Pointer，vallen*\uSocklen）（error error）=libsocket=libsocket.getpeername 
// sys-getsockname（fd int，rsa*RawSockaddrAny，addrlen*_Socklen）（error error）=libsocket.getsockname 
// sys-setsockopt（s int，level int，name int，val unsafe.Pointer，vallen uintptpr）（error）=libsocket.setsockopt 
// sys recvmsg（s int，msg*Msghdr，标志int）（n int，error error）=libsocket.\uxnet\u recvmsg 
// sys utimensat（dirfd int，路径字符串，times*[2]Timespec，flag int）（错误）

func Getexecname() (path string, err error) {
	ptr, err := getexecname()
	if err != nil {
		return "", err
	}
	bytes := (*[1 << 29]byte)(ptr)[:]
	for i, b := range bytes {
		if b == 0 {
			return string(bytes[:i]), nil
		}
	}
	panic("unreachable")
}

func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_read)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_write)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

func Utimes(path string, tv []Timeval) error {
	if len(tv) != 2 {
		return EINVAL
	}
	return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
